package gates;

/**
 * Factory class to produce gate objects. Constructors for the gates should be private so this
 * should be the only way to create gate objects.
 * The static boolean <code>functional</code> determines if matrix or functional representations
 * of the gates are return, depending on what is available to use.
 * E.g. if <code>functional</code> is true and no functional representation is available, then
 * a matrix representation is returned.
 * Since all complex gates can be constructed from multiple primitive gates, the gates available
 * should be able to construct any quantum circuit.
 * @author Alex Bush, Jan Zaucha
 *
 */
public final class Factory {
	/**
	 * Static boolean that determines is a function representation is returned or not.
	 * By default, this is set to true as there are performance benefits, however majority of gates
	 * will only have a matrix representation.
	 */
	public static boolean functional = true;


	/**
	 * Overwrite the global <code>functional</code> variable for all hadamard gates.
	 * Default for <code>hadamardmatrix</code> is false
	 */
	public static boolean hadamardmatrix = false;
	/**
	 * Create a hadamard <code>Gate</code> object and return to the user.
	 * Requires a target bit to operate. If the gate acts on the whole
	 * register, target bit is -1.
	 * @param target Target bits
	 */
	public static Gate createHadamard(int target){
		if (hadamardmatrix){
			return (Gate)new HadamardMatrix(target);
		}
		else if(functional){
			return (Gate)new HadamardFunction(target);
		}
		else {
			return (Gate)new HadamardMatrix(target);
		}
	}

	/**
	 * Overwrite the global <code>functional</code> variable for all CNot gates.
	 * Default for <code>cnotmatrix</code> is false
	 */
	public static boolean cnotmatrix = false;
	/**Create a CNot <code>Gate</code> object and return to the user.
	 * 
	 * @param control - control bit
	 * @param target - target bit
	 */
	public static Gate createCNot (int control, int target){
		if (cnotmatrix){
			return (Gate)new CnotMatrix(control,target);
		}
		else if (functional){
			return (Gate)new CnotFunction(control,target);
		}
		else {
			return (Gate)new CnotMatrix(control,target);
		}
	}

	/**
	 * Overwrite the global <code>functional</code> variable for all phase gates.
	 * Default for <code>phasematrix</code> is false
	 */
	public static boolean phasematrix = false;
	/**Create a Phase Shift <code>Gate</code> object and return to the user.
	 * 
	 * @param target - target qubit
	 * @param phi - angle of phase shift
	 */
	public static Gate createPhaseShift (int target, double phi){
		if (phasematrix){
			return (Gate)new PhaseShiftMatrix(target,phi);
		}
		else if (functional){
			return (Gate)new PhaseShiftFunction(target,phi);
		}
		else{
			return (Gate)new PhaseShiftMatrix(target,phi);
		}
	}

	/**
	 * Create a composite <code>Gate</code> object and return to the user.
	 * Must provide an array of Gates to create a composite gate. Any of these gates can be
	 * a composite gate itself.
	 * @param gates Array of gate objects
	 */
	public static Gate createComposite(Gate[] gates){
		return new CompositeGate(gates);
	}

	
	/**
	 * Create a composite GroverIteration <code>Gate</code> object and return to the user.
	 * All embedded gates are created automatically and this iteration is self containing.
	 * Gates are created using this Factory class so usual rules of <code>functional/matrix</code> representation
	 * apply.
	 * @param searched Searched element in the algorithm
	 * @param numQubits Number of qubits in the algorithm
	 */
	public static Gate createGroverIteration(int searched,int numQubits){
		return new GroverComposite(searched, numQubits);
	}
	
	/**
	 * Overwrite the global <code>functional</code> variable for all iteration gates.
	 * Default for <code>oraclematrix</code> is false and hence fall back onto <code>functional</code> boolean
	 */
	public static boolean oraclematrix = false;

	/**
	 * Create an oracle gate, used in the functional representation of the groverFuncion gate.
	 * @param searched Searched state the iteration is looking for
	 */
	public static Gate createOracle(int searched){
		if (oraclematrix){
			return (Gate)new OracleMatrix(searched);
		}
		else if (functional){
			return (Gate)new OracleFunction(searched);
		}
		else{
			return (Gate)new OracleMatrix(searched);
		}
	}
	
	/**
	 * Overwrite the global <code>functional</code> variable for all iteration gates.
	 * Default for <code>complexImatrix</code> is false and hence fall back onto <code>functional</code> boolean
	 */
	public static boolean complexImatrix = false;
	
	/**
	 * Create a iI gate, used in the representation of the grover iteration in terms of universal gates.
	 * @param target Target qubit
	 */
	public static Gate createComplexIdentity(int target){
		if (complexImatrix){
			return (Gate)new ComplexIdentityMatrix(target);
		}
		else if (functional){
			return (Gate)new ComplexIdentityFunction(target);
		}
		else{
			return (Gate)new ComplexIdentityMatrix(target);
		}

	}
	/**
	 * Overwrite the global <code>groverbasicgates</code> variable for all grover iteration gates.
	 * Default for <code>groverbasicgates</code> is false and hence gates are most efficient by default
	 * This boolean superseeds <code>grovermatrix</code> and <code>functional</code> so if <code>groverbasicgates</code>, <code>grovermatrix</code> and <code>functional</code>
	 * are all set to true, the grover representation will be of basic gates.
	 */
	public static boolean groverbasicgates = false;
	
	
	/**
	 * Create a nand gate that flips all states except the first.
	 *  @param numQubits Number of qubits in register 
	 */
	public static Gate createNand(int numQubits){
		if(groverbasicgates){
			return new NandComposite(numQubits);
		}
		else if(functional){
			return new NandAllFunction();
		}
		
		else{
			return new NandAllMatrix();
		}
	}
	/**
	 * Overwrite the global <code>functional</code> variable for all not gates.
	 * Default for <code>notmatrix</code> is false
	 */
	public static boolean notmatrix=false;
	/**
	 * Create a NOT gate
	 * @param target Target qubit of the gate
	 */
	public static Gate createNot(int target){
		if(notmatrix){
			return (Gate)new NotMatrix(target);
		}
		else if (functional){
			return (Gate)new NotFunction(target);
		}
		else{
			return (Gate)new NotMatrix(target);
		}
	}

	/**
	 * Create a new measurement gate.
	 */
	public static Gate createMeasurement(){
		return new Measurement();
	}
	/**
	 * Overwrite the global <code>functional</code> variable for all allcnot gates.
	 * Default for <code>allcnotmatrix</code> is false
	 */
	public static boolean allcnotmatrix=false;
	
	/**
	 * Create an All-C Not gate.
	 * @param target Target qubit of the gate
	 */
	public static Gate createAllCnot(int target){
		if (allcnotmatrix){
			return (Gate)new AllCnotMatrix(target);
		}
		else if (functional){
			return (Gate)new AllCnotFunction(target);
		}
		else{
			return (Gate)new AllCnotMatrix(target);
		}
	}

	/**
	 * Overwrite the global <code>functional</code> variable for all Z Pauli gates.
	 * Default for <code>Zmatrix</code> is false
	 */
	public static boolean Zmatrix = false;
	/**Create a Z <code>Gate</code> object and return to the user.
	 * 
	 * @param target - target qubit
	 */
	public static Gate createZ(int target){
		if (Zmatrix){
			return (Gate)new ZMatrix(target);
		}
		else if (functional){
			return (Gate)new ZFunction(target);
		}
		else{
			return (Gate)new ZMatrix(target);
		}
	}
	
	/**
	 * Overwrite the global <code>functional</code> variable for all Y Pauli gates.
	 * Default for <code>Ymatrix</code> is false
	 */
	public static boolean Ymatrix = false;
	/**Create a Z <code>Gate</code> object and return to the user.
	 * 
	 * @param target - target qubit
	 */
	public static Gate createY(int target){
		if (Ymatrix){
			return (Gate)new YMatrix(target);
		}
		else if (functional){
			return (Gate)new YFunction(target);
		}
		else{
			return (Gate)new YMatrix(target);
		}
	}

	/**
	 * Create a T gate, used for creating a toffoli out of composite gates
	 * @param target Target qubit to act on
	 */
	public static Gate createT(int target){
		return new TEnclosing(target);
	}

	/**
	 * Create a Tdagger gate, used for creating a toffoli out of composite gates
	 * @param target Target qubit to act on
	 */
	public static Gate createTDagger(int target){
		return new TDaggerEnclosing(target);
	}

	/**
	 * Create a S gate, used for creating a toffoli out of composite gates
	 * @param target Target qubit to act on
	 */
	public static Gate createS(int target){
		return new SEnclosing(target);
	}
	/**
	 * Overwrite the global <code>basicgates</code> variable for all Toffoli gates.
	 * Default for <code>Toffolibasicgates</code> is false
	 */
	public static boolean Toffolibasicgates = false;

	/**
	 * Create a Toffoli gate or a C-C-Not gate
	 * @param control1 First control bit
	 * @param control2 Second control bit
	 * @param target Target bit
	 */
	public static Gate createToffoli(int control1, int control2, int target){
		if (Toffolibasicgates){
			return new ToffoliComposite(control1, control2, target);
		}
		else{
			return new ToffoliFunction(control1, control2, target);
		}
	}
}
